/*
 Copyright (C) 2010 Kristian Duske

 This file is part of TrenchBroom.

 TrenchBroom is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 TrenchBroom is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "render/GL.h"
#include "render/MaterialIndexArrayMap.h"

#include <vector>

namespace tb
{
namespace mdl
{
class Material;
}

namespace render
{
/**
 * Builds index array map by recording rendering primitives. The corded data can be used
 * to create an index array that can be uploaded to video card memory, and to render the
 * recorded primitives with the indices from that array using the constructed index array
 * map.
 */
class MaterialIndexArrayMapBuilder
{
public:
  using Material = mdl::Material;
  using Index = GLuint;
  using IndexList = std::vector<Index>;

private:
  IndexList m_indices;
  MaterialIndexArrayMap m_ranges;

public:
  /**
   * Creates a new builder with the internal index array map initialized to the
   * given size.
   *
   * @param size the size to initialize to
   */
  explicit MaterialIndexArrayMapBuilder(const MaterialIndexArrayMap::Size& size);

  /**
   * Returns the recorded indices.
   *
   * @return the recorded indices
   */
  IndexList& indices();

  /**
   * Returns the recorded index ranges for the primitives that were added.
   *
   * @return the recorded index ranges
   */
  MaterialIndexArrayMap& ranges();

  /**
   * Adds a point, represented by a vertex in a vertex array at the given index.
   *
   * @param material the material to use
   * @param i the index to record
   */
  void addPoint(const Material* material, Index i);

  /**
   * Adds multiple points, represented by the vertices in a vertex array at the
   * given indices.
   *
   * @param material the material to use
   * @param indices the indices to record
   */
  void addPoints(const Material* material, const IndexList& indices);

  /**
   * Adds a line, represented by the vertices in a vertex array at the given two
   * indices.
   *
   * @param material the material to use
   * @param i1 the index of the start vertex to record
   * @param i2 the index of the end vertex to record
   */
  void addLine(const Material* material, Index i1, Index i2);

  /**
   * Adds multiple lines, each represented by two vertices in a vertex array. The
   * given index array contains pairs of indices, where each pair consists of the index of
   * the first and the index of the second vertex.
   *
   * @param material the material to use
   * @param indices a list of indices containing the pairs of vertex indices to record
   */
  void addLines(const Material* material, const IndexList& indices);

  /**
   * Adds a triangle, represented by the vertices in a vertex array at the given
   * indices.
   *
   * @param material the material to use
   * @param i1 the index of the first vertex to record
   * @param i2 the index of the second vertex to record
   * @param i3 the index of the third vertex to record
   */
  void addTriangle(const Material* material, Index i1, Index i2, Index i3);

  /**
   * Adds multiple triangles, each represented by three vertices in a vertex
   * array. The given index array contains triples of indices, where each triple consists
   * of the indices of the three vertices making up the triangle to add.
   *
   * @param material the material to use
   * @param indices a list of indices containing the triples of vertex indices to record
   */
  void addTriangles(const Material* material, const IndexList& indices);

  /**
   * Adds a quad, represented by the vertices in a vertex array at the given
   * indices.
   *
   * @param material the material to use
   * @param i1 the index of the first vertex to record
   * @param i2 the index of the second vertex to record
   * @param i3 the index of the third vertex to record
   * @param i4 the index of the fourth vertex to record
   */
  void addQuad(const Material* material, Index, Index i1, Index i2, Index i3, Index i4);

  /**
   * Adds multiple quads, each represented by four vertices in a vertex array.
   * The given index array contains four-tuples of indices, where each tuple consists of
   * the indices of the four vertices making up the quad to add.
   *
   * @param material the material to use
   * @param indices a list of indices containing the four-tuples of vertex indices to
   * record
   */
  void addQuads(const Material* material, const IndexList& indices);

  /**
   * Adds multiple quads by adding a range of indices specified by the given base
   * index and length. Specifically, a call to this method records a sequence of indices
   * which is computed using the given base index and vertex count as follows.
   *
   * index 1 = baseIndex + 0;
   * index 2 = baseIndex + 1;
   * ...
   * index n = baseIndex + vertexCount - 1;
   *
   * @param material the material to use
   * @param baseIndex the base index at which to start the range
   * @param vertexCount the number of vertices contained in the range
   */
  void addQuads(const Material* material, Index baseIndex, size_t vertexCount);

  /**
   * Adds a polygon with the given indices. Note that the polygon is translated
   * to a set of triangles and no actual polygon is recorded at all.
   *
   * @param material the material to use
   * @param indices the indices of the vertices making up the polygon to add
   */
  void addPolygon(const Material* material, const IndexList& indices);

  /**
   * Adds a polygon with indices computed from the given range. The polygons
   * vertices are expected to be stored sequentially in a vertex array, starting at the
   * given base index. The given vertex count indicates the number of vertices to add.
   * Note that the polygon is translated to a set of triangles.
   *
   * @param material the material to use
   * @param baseIndex the index of the first vertex of the polygon
   * @param vertexCount the number of vertices of the polygon
   */
  void addPolygon(const Material* material, Index baseIndex, size_t vertexCount);

private:
  void add(const Material* material, PrimType primType, const IndexList& indices);
};

} // namespace render
} // namespace tb
